/*
 * ValidationRule.h
 *
 *  Created on: 01/06/2013
 *      Author: Jeffrey Alvarez Masson
 */

#ifndef VALIDATIONRULE_H_
#define VALIDATIONRULE_H_

#include <boost/foreach.hpp>
#include <stdexcept>
#include <vector>
#include <string>

namespace ValidationRules {

/**
 * Represents a validation rule.
 */
class ValidationRule {
protected:
	/**
	 * Gets the preceding rule name required to apply.
	 * @return Name of the rule required to be applied first.
	 */
	std::string GetRequiredPrecedingRule(std::string preceding);
public:
	/**
	 * Creates a new instance of the ValidationRule class.
	 * @param preceding Names of the preceding validation rules, separated by comma.
	 */
	ValidationRule() { }
	/**
	 * Disposes resources.
	 */
	virtual ~ValidationRule() {}
	/**
	 * Gets the rule name or unique ID.
	 * @return Rule name.
	 */
	virtual std::string GetRuleName() = 0;
	/**
	 * Gets a string representation of the boolean expression of the rule.
	 * @return String representation of the expression.
	 */
	virtual std::string GetRuleExpression() = 0;
	/**
	 * Gets the result of the last validation (evaluation) of the expression.
	 * @return Last validation (evaluation) of the expression.
	 */
	virtual bool GetLastValidationResult() = 0;
	/**
	 * Gets the times the rule was applied.
	 * @return Number of times the rule was applied.
	 */
	virtual int GetTimesApplied() = 0;
};

/**
 * Define a specific validation rule.
 * @param id ID or name of the rule (must be unique).
 * @param pred String with the preceding validation rules' names, separated by comma.
 * @param expr Boolean expression that represents the rule.
 * @param Tdef Variable declaration of the object the rule is based on.
 * @param args Variable(s) declaration(s) of the argument(s) involved in the expression.
 */
#define DEFINE_RULE_ARGS(id,pred,expr,Tdef,args) \
	class VR__##id : public ValidationRules::ValidationRule { \
		private: \
			int timesApplied; \
			bool result; \
			std::string required; \
			static std::string preceding; \
		public: \
			VR__##id(): timesApplied(0), result(false) {} \
			virtual ~VR__##id() {} \
			bool Validate(Tdef, args) { \
				this->required = this->GetRequiredPrecedingRule(VR__##id::preceding); \
				if (!this->required.empty()) \
					throw std::logic_error("Rule '" + this->required + "' required before '" + this->GetRuleName() + "'."); \
				this->result = expr; \
				return this->result; \
			} \
			virtual std::string GetRuleName() { return #id; } \
			virtual std::string GetRuleExpression() { return #expr; } \
			virtual bool GetLastValidationResult() { return result; } \
			virtual int GetTimesApplied() { return timesApplied; } \
			void Apply() { ++timesApplied; } \
	}; \
	std::string VR__##id::preceding = pred;

/**
 * Define a specific validation rule.
 * @param id ID or name of the rule (must be unique).
 * @param pred String with the preceding validation rules' names, separated by comma.
 * @param expr Boolean expression that represents the rule.
 * @param Tdef Variable declaration of the object the rule is based on.
 */
#define DEFINE_RULE(id,pred,expr,Tdef) DEFINE_RULE_ARGS(id,pred,expr,Tdef,...)

/**
 * Do-nothing action (to put as onTrue or onFalse parameter).
 */
#define GO_ON {}

/**
 * Receives a boolean expression and performs an action according to the result.
 * @param r Boolean expression.
 * @param onTrue When the expression evaluates to true, execute this command.
 * @param onFalse When the expression evaluates to false, execute this command.
 */
#define APPLY_RULE_EXPR(r,onTrue,onFalse) { \
	if (r) { onTrue; } else { onFalse; } }

/**
 * Apply a specific validation rule with arguments.
 * @param id Name of the rule.
 * @param obj Object on which the expression is based.
 * @param onTrue When the expression evaluates to true, execute this command.
 * @param onFalse When the expression evaluates to false, execute this command.
 * @param args Additional argument(s) required to evaluate the expression.
 */
#define APPLY_RULE_ARGS(id,obj,onTrue,onFalse,args) { \
	VR__##id* r##id = (VR__##id*)ValidationRules::ValidationScope::GetCurrent()->GetRule(#id); \
	if (!r##id) { \
		r##id = new VR__##id(); \
		ValidationRules::ValidationScope::GetCurrent()->AddRule(ValidationRuleSPtr(r##id)); } \
	APPLY_RULE_EXPR((r##id)->Validate(obj,args), \
		{(r##id)->Apply(); {onTrue;}}, \
		{(r##id)->Apply(); {onFalse;}}); }   //

/**
 * Apply a specific validation rule.
 * @param id Name of the rule.
 * @param obj Object on which the expression is based.
 * @param onTrue When the expression evaluates to true, execute this command.
 * @param onFalse When the expression evaluates to false, execute this command.
 */
#define APPLY_RULE(id,obj,onTrue,onFalse) \
		APPLY_RULE_ARGS(id,obj,onTrue,onFalse,1)

/**
 * Apply a specific validation rule on the elements of a list.
 * @param list List with the elements to validate.
 * @param Telem Variable declaration of the element to validate.
 * @param r Command (APPLY_RULE_*) to execute for each element.
 */
#define FOREACH_APPLY_RULE(list,Telem,r) { \
	BOOST_FOREACH(Telem, list) { r; }}

#define VRG(r) ( \
	ValidationRules::ValidationScope::GetCurrent()->GetRule(#r))

/**
 * Gets the last evaluation result for a rule.
 * @param r Name of the rule.
 */
#define VRE(r) ( \
	(VRG(r))->GetLastValidationResult() )

} /* namespace ValidationRules */
#endif /* VALIDATIONRULE_H_ */
